﻿/*
   Copyright 2012 University of Southampton

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint.WebControls;
using uk.ac.soton.ses.DAL;
using System.Web;
using System.Linq;
using uk.ac.soton.ses.UserControls;
using System.Collections.Generic;

namespace uk.ac.soton.ses.ExperimentLinksWebPart
{
    public partial class ExperimentLinksWebPartUserControl : UserControl
    {
        /// <summary>
        /// A private class for populating the data for the grid
        /// </summary>
        private class ExperimentLink_ViewData
        {
            //Fields for properties below
            private Experiment _dbParentExperiment;
            private Experiment _dbLinkedExperiment;
            private ExperimentLinkDirection _relativeDirection;
            private Experiment _rootExperiment;
            private Experiment _otherExperiment;

            /// <summary>
            /// The parent experiment in the link, as stored in the database
            /// </summary>
            public Experiment DBParentExperiment { get { return this._dbParentExperiment; } }

            /// <summary>
            /// The linked experiment in the link, as stored in the database
            /// </summary>
            public Experiment DBLinkedExperiment { get { return this._dbLinkedExperiment; } }

            /// <summary>
            /// The ID of the parent experiment in the link, as stored in the database
            /// </summary>
            public int DBParentExperimentID { get { return this._dbParentExperiment.ID; } }

            /// <summary>
            /// The ID of the linked experiment in the link, as stored in the database
            /// </summary>
            public int DBLinkedExperimentID { get { return this._dbLinkedExperiment.ID; } }

            /// <summary>
            /// The direction the link is made, relative to the displayed experiment
            /// </summary>
            public ExperimentLinkDirection RelativeDirection { get { return this._relativeDirection; } }

            /// <summary>
            /// The displayed experiment, calculated using the direction of the link
            /// </summary>
            public Experiment RootExperiment { get { return this._rootExperiment; } }

            /// <summary>
            /// The other experiment, calculated using the direction of the link
            /// </summary>
            public Experiment OtherExperiment { get { return this._otherExperiment; } }

            /// <summary>
            /// The ID of the displayed experiment in the link, calculated using the direction of the link
            /// </summary>
            public int RootExperimentID { get { return this._rootExperiment.ID; } }

            /// <summary>
            /// The ID of the other experiment in the link, calculated using the direction of the link
            /// </summary>
            public int OtherExperimentID { get { return this._otherExperiment.ID; } }

            /// <summary>
            /// Data for list view, recording the link stored in the database and its direction in relation to the displayed experiment
            /// </summary>
            /// <param name="dbParentExperiment">The parent experiment in the link, as stored in the database</param>
            /// <param name="dbLinkedExperiment">The linked experiment, as stored in the database</param>
            /// <param name="relativeDirection">The direction the link is made, relative to the displayed experiment</param>
            public ExperimentLink_ViewData(Experiment dbParentExperiment, Experiment dbLinkedExperiment, ExperimentLinkDirection relativeDirection)
            {
                this._dbParentExperiment = dbParentExperiment;
                this._dbLinkedExperiment = dbLinkedExperiment;
                this._relativeDirection = relativeDirection;

                //Set the root experiment and other experiment based on the relative link direction
                if (RelativeDirection == ExperimentLinkDirection.LinkOut || RelativeDirection == ExperimentLinkDirection.LinkBoth)
                {
                    this._rootExperiment = this.DBParentExperiment;
                    this._otherExperiment = this.DBLinkedExperiment;
                }
                else
                {
                    this._rootExperiment = this.DBLinkedExperiment;
                    this._otherExperiment = this.DBParentExperiment;
                }
            }
        }

        /// <summary>
        /// A private enum for determining which direction a link is made
        /// </summary>
        private enum ExperimentLinkDirection : int
        {
            LinkOut = 0,
            LinkIn = 1,
            LinkBoth = 2
        }

        /// <summary>
        /// Field set earlier in the life cycle to indicate whether we will rebind the view later in the lifecycle (e.g. OnPreRender)
        /// </summary>
        private bool _rebindRequired = false;

        /// <summary>
        /// Database context
        /// </summary>
        MaterialsMetadataContext _dbcontext = null;

        /// <summary>
        /// Used to record the experiment being bound
        /// </summary>
        private int _experimentID = -999;

        /// <summary>
        /// Configures the control with the experiment ID being bound
        /// </summary>
        public int ExperimentID { get { return this._experimentID; } set { this._experimentID = value; } }

        #region Page life cycle methods

        /// <summary>
        /// OnInit in ASP.NET page life cycle overridden. We configure events here.
        /// </summary>
        /// <param name="e">Event arguments received by events</param>
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            this._dbcontext =  HttpContext.Current.GetMaterialsDBContext();

            //Set up gridview's events
            this.ExperimentLinksListView.ItemDataBound +=new EventHandler<ListViewItemEventArgs>(ExperimentLinksListView_ItemDataBound);
            this.ExperimentLinksListView.ItemDeleting+=new EventHandler<ListViewDeleteEventArgs>(ExperimentLinksListView_ItemDeleting);

            //Handle ExperimentSelected event from QuickSearch control
            ExperimentQuickSearch linkSearch = this.Control_ExperimentQuickSearch_Link as ExperimentQuickSearch;
            linkSearch.ExperimentSelected += new ExperimentSelectedEventHandler(LinkToExistingExperiment);
            
            //Ensure the HDC style sheet is added to the page
            this.Page.AddHDCStylesheet();
        }

        /// <summary>
        /// Page_Load in ASP.NET page life cycle. We databind controls here.
        /// </summary>
        /// <param name="sender">Usual sender object received by events</param>
        /// <param name="e">Event arguments received by events</param>
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (this._experimentID < 0)
            {
                HDCSharedWebFunctions.AddErrorLabel("Experiment ID not set", this.PlaceHolder_Messages);
            }
            else
            {
                //We are using ViewState so only rebind on first load (or after an event)
                if (!Page.IsPostBack)
                {
                    this.DataBindExperimentLinks();
                }
            }
        }

        /// <summary>
        /// OnPreRender in ASP.NET page life cycle. We will rebind the grid here if the earlier control events requested it.
        /// </summary>
        /// <param name="e">Event arguments received by events</param>
        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (this._rebindRequired)
            {
                this.DataBindExperimentLinks();
            }
        }

        #endregion

        #region Data binding methods

        /// <summary>
        /// Data bind the main grid of experiment links
        /// </summary>
        private void DataBindExperimentLinks()
        {
            //Check we have an experiment ID to bind against
            if (this._experimentID > 0)
            {
                //Bind experiment collection
                List<ExperimentLink_ViewData> linkedExperiments = new List<ExperimentLink_ViewData>();
                linkedExperiments.AddRange(this._dbcontext.GetReferencedExperimentLinks(this._experimentID).Select(link => new ExperimentLink_ViewData(link.ExperimentParent, link.ExperimentLinked,ExperimentLinkDirection.LinkOut)));
                linkedExperiments.AddRange(this._dbcontext.GetReferencingExperimentLinks(this._experimentID).Select(link => new ExperimentLink_ViewData(link.ExperimentParent, link.ExperimentLinked, ExperimentLinkDirection.LinkIn)));
                linkedExperiments.AddRange(this._dbcontext.GetTwoWayLinkedExperiments(this._experimentID).Select(link => new ExperimentLink_ViewData(link.ExperimentParent, link.ExperimentLinked, ExperimentLinkDirection.LinkBoth)));

                this.ExperimentLinksListView.DataSource = linkedExperiments.OrderBy(link => link.OtherExperiment.Name).ThenBy(link => link.OtherExperiment.ID);
                this.ExperimentLinksListView.DataBind();
            }
        }

        #endregion

        #region Control events

        /// <summary>
        /// Called once per row of experiment links grid, used to work out which way a link goes and bind the experiment names and arrow direction
        /// </summary>
        /// <param name="sender">The control that generated the event</param>
        /// <param name="e">Event arguments received by events</param>
        private void ExperimentLinksListView_ItemDataBound(object sender, ListViewItemEventArgs e)
        {
            ListViewDataItem gvr = e.Item as ListViewDataItem;

            //Check we have a row, otherwise don't bind anything
            if (gvr == null)
            {
                return;
            }

            //Get the experiment link data
            ExperimentLink_ViewData experimentLinkData = gvr.DataItem as ExperimentLink_ViewData;

            //Check we have data
            if (experimentLinkData == null)
            {
                return;
            }

            //Get the controls we need to modify
            Label thisExperimentLabel = gvr.FindControl("lbl_ThisExperimentName") as Label;
            DropDownList linkDirectionList = gvr.FindControl("ddl_LinkDirection") as DropDownList;
            HyperLink targetExperimentHyperLink = gvr.FindControl("lnk_TargetExperimentName") as HyperLink;
            Label targetExperimentDescriptionLabel = gvr.FindControl("lbl_TargetExperimentDescription") as Label;

            //Check we have the controls before binding anything
            if (thisExperimentLabel == null || targetExperimentHyperLink == null || linkDirectionList == null || targetExperimentDescriptionLabel == null)
            {
                return;
            }

            //Set the arrow in the dropdown to point the correct way
            linkDirectionList.SelectedValue = ((int)experimentLinkData.RelativeDirection).ToString();

            //Add the correct experiment names
            thisExperimentLabel.Text = experimentLinkData.RootExperiment.GetNameAndID();
            targetExperimentHyperLink.Text = experimentLinkData.OtherExperiment.GetNameAndID();

            //Set the destination url for the target experiment
            targetExperimentHyperLink.NavigateUrl = HDCConfiguration.GetUrl_ExperimentDetails(experimentLinkData.OtherExperiment.ID);

            //Set the description for the target experiment
            targetExperimentDescriptionLabel.Text = experimentLinkData.OtherExperiment.Description;
        }

        /// <summary>
        /// Called when the user changes the direction of the link
        /// </summary>
        /// <param name="sender">The dropdown that generated the event</param>
        /// <param name="e">Event arguments received by events</param>
        protected void ddl_LinkDirection_SelectedIndexChanged(object sender, EventArgs e)
        {
            //Get the dropdown so we can see what was selected
            DropDownList ddl = sender as DropDownList;
            if (ddl == null)
            {
                return;
            }

            //Parse the selected value so we can use it in a case statement to compare against ExperimentLinkDirection
            string newDirectionStr = ddl.SelectedValue;
            int newDirection = -1;
            if (!int.TryParse(newDirectionStr, out newDirection))
            {
                return;
            }

            //Get the listview row so we can get to the data keys
            ListViewDataItem di = ddl.NamingContainer as ListViewDataItem;
            if (di == null)
            {
                return;
            }

            //Lookup the data keys of the listview row
            int parentExperimentID = (int)this.ExperimentLinksListView.DataKeys[di.DataItemIndex]["DBParentExperimentID"];
            int linkedExperimentID = (int)this.ExperimentLinksListView.DataKeys[di.DataItemIndex]["DBLinkedExperimentID"];
            int thisExperimentID = (int)this.ExperimentLinksListView.DataKeys[di.DataItemIndex]["RootExperimentID"];
            int otherExperimentID = (int)this.ExperimentLinksListView.DataKeys[di.DataItemIndex]["OtherExperimentID"];

            //Track changes so we can decide if to save
            bool changed = false;
            
            //Edit the existing links depending on the user's selection from the dropdown
            switch (newDirection)
            {
                case (int)ExperimentLinkDirection.LinkOut:
                    //Delete any link in
                    changed |= this._dbcontext.DeleteExperimentLink(otherExperimentID, thisExperimentID, false);
                    //Create the link out
                    changed |= this._dbcontext.CreateExperimentLink(thisExperimentID, otherExperimentID, false);
                    break;

                case (int)ExperimentLinkDirection.LinkIn:
                    //Delete the link out
                    changed |= this._dbcontext.DeleteExperimentLink(thisExperimentID, otherExperimentID, false);
                    //Create the link in
                    changed |= this._dbcontext.CreateExperimentLink(otherExperimentID, thisExperimentID, false);
                    break;

                case (int)ExperimentLinkDirection.LinkBoth:
                    //Create the link in
                    changed |= this._dbcontext.CreateExperimentLink(otherExperimentID, thisExperimentID, false);
                    //Create the link out
                    changed |= this._dbcontext.CreateExperimentLink(thisExperimentID, otherExperimentID, false);
                    break;
            }

            //Save changes if required
            if (changed) this._dbcontext.SaveChanges();

            //Rebind the grid
            this._rebindRequired = true;
        }

        /// <summary>
        /// Called when a user removes the link between experiments
        /// </summary>
        /// <param name="sender">The control that generated the event (the listview)</param>
        /// <param name="e">Event arguments received by events</param>
        private void ExperimentLinksListView_ItemDeleting(object sender, ListViewDeleteEventArgs e)
        {
            //Get the GridView so we can work out which row was deleted
            System.Web.UI.WebControls.ListView gv = (System.Web.UI.WebControls.ListView)sender;

            //Find out which link is being deleted
            int parentExperimentID = (int)gv.DataKeys[e.ItemIndex]["DBParentExperimentID"];
            int linkedExperimentID = (int)gv.DataKeys[e.ItemIndex]["DBLinkedExperimentID"];
            ExperimentLinkDirection currentDirection = (ExperimentLinkDirection)gv.DataKeys[e.ItemIndex]["RelativeDirection"];
            bool changed = false;
            changed |= this._dbcontext.DeleteExperimentLink(parentExperimentID, linkedExperimentID, false);

            if (currentDirection == ExperimentLinkDirection.LinkBoth)
            {
                changed |= this._dbcontext.DeleteExperimentLink(linkedExperimentID, parentExperimentID, false);
            }

            //Save changes if required
            if (changed) this._dbcontext.SaveChanges();

            //Always rebind the grid (because deleting a link may be overridden by a change in direction)
            this.DataBindExperimentLinks();
        }

        /// <summary>
        /// Generated when the user selects an experiment from the quick search box. We will create a new link based on the user's choice.
        /// </summary>
        /// <param name="sender">The quick search control</param>
        /// <param name="e">Event argument containing the user's experiment selection</param>
        private void LinkToExistingExperiment(object sender, ExperimentSelectedEventArgs e)
        {
            //Create the link
            this._dbcontext.CreateExperimentLink(this._experimentID, e.SelectedExperimentID, true);

            //Rebind the grid
            this._rebindRequired = true;
        }

        #endregion

    }
}
